Now Loading ...
-
-
1. blog
블로그를 만든 기념으로 파이썬을 이용해서 블로그가 운영되는 형식에 맞춰서 파이썬 사용법을 연습해봄
main.py
{% highlight python %}
from module.Blog import Blog
from module.Post import Post
MAIN_TEXT = ‘’’
블로그 글 작성
블로그 글 리스트
종료
’’’
MAIN1_TEXT = [
“타이틀: “,
“내용: “
]
MAIN2_TEXT = ‘’’
—————–
블로그 보기
블로그 편집
뒤로 돌아가기
’’’
MAIN2_DETAIL_TEXT = ‘id를 입력해주세요: ‘
MAIN1_END_TEXT = ‘’’
작성 완료했습니다.
——————
‘’’
class Main:
def init(self, blog_data) -> None:
self.blog_data = blog_data
pass
def call(self):
while True:
try:
handler = Handler()
post = Post()
blog = Blog()
(menu_num, input_data) = handler.get_input(text = MAIN_TEXT)
input_data = int(input_data)
if menu_num == “main”:
if input_data == 1:
(menu_num, input_datas) = handler.get_input(text = MAIN1_TEXT)
self.blog_data = post.create(data= self.blog_data, title= input_datas[0], content= input_datas[1])
print(MAIN1_END_TEXT)
elif input_data == 2:
(menu_num, input_data) = handler.get_input(text = MAIN2_TEXT)
blog.view_all(data= self.blog_data, state=”title”)
input_data = int(input_data)
elif input_data == 3:
break
else:
print("다시 선택해주세요.")
if menu_num == "main2":
if input_data == 1:
(menu_num, input_data) = handler.get_input(text = MAIN2_DETAIL_TEXT)
elif input_data == 2:
pass
elif input_data == 3:
pass
else:
print("다시 선택해주세요.")
if menu_num == "main2_1":
blog.view_all(data= self.blog_data, state="full", id=int(input_data))
(menu_num, input_data) = handler.get_input(text = "")
except ValueError:
print("숫자를 입력해주세요.")
class BlogDataSetting:
def init(self) -> None:
self.max_id = self.__get_default_id()
self.posts = self.__get_posts()
pass
def __get_default_id(self):
return 0
def __get_posts(self):
return []
class Handler:
def get_input(self, text):
if (text == MAIN_TEXT):
print(text)
return (“main”, input())
elif (text == MAIN1_TEXT):
input_list = []
for txt in text:
input_list.append(input(txt))
return (“main1”, input_list)
elif (text == MAIN2_TEXT):
print(text)
return (“main2”, input())
elif (text == MAIN2_DETAIL_TEXT):
return (“main2_1”, input(text))
else:
return (“main”, input(“돌아가기”))
if name == “main”:
blog_data = BlogDataSetting()
main = Main(blog_data)
main()
{% endhighlight %}
module/Blog.py
{% highlight python %}
class Blog():
“””
전체 리스트 보기(id 기준), 태그 리스트 보기
“””
def init(self) -> None:
super().init()
pass
def view_all(self, data, state="title", id=False):
posts = data.posts
print("-------------")
for post in posts:
if id:
if id == post['id']:
print(f"id: {post['id']}")
print(f"타이틀: {post['title']}")
if state == "full":
post['view_count'] += 1
print(f"내용: {post['content']}")
print(f"view: {post['view_count']}")
print(f"like: {post['like']}")
print(f"\t")
else:
print(f"id: {post['id']}")
print(f"타이틀: {post['title']}")
if state == "full":
print(f"내용: {post['content']}")
print(f"view: {post['view_count']}")
print(f"like: {post['like']}")
print(f"\t")
print("-------------") {% endhighlight %}
module/Post.py
{% highlight python %}
class Post():
def __init__(self) -> None:
super().__init__()
pass
def create(self, data, title, content):
data.max_id += 1
data.posts.append({
"id": data.max_id,
"title": title,
"content": content,
"view_count": 0,
"like": 0
})
return data
def edit():
pass {% endhighlight %}
-
1. essay quality
kaggle-linking-writing-processes-to-writing-quality
Overview
글쓰기 품질을 예측하기 위한 모델을 만드는 대회이다.
데이터는 키스트로크 로그 데이터로 이루어져 있다.(시계열)
Description
글쓰기 과정을 정형데이터로 만들기가 쉽지 않다. 에세이별로 작성자의 특성(쉬는 타이밍, 수정법, …)을 파악하여 품질에 영향을 줄 수 있다. 하지만 실제는 결과물만 가지고 평가를 하게 되어서 실질적인 활용에 있어서는 고려해야할 사항이 많다.
Evaluation
평가지표로는 RMSE평가지표를 기반으로 사용한다. 이번대회는 특이하게 Efficiency RMSE 평가지표도 활용하여 추가 수상의 기회가 있다.
Data Collection Procedure
Keystroke Data Collection Procedure
SAT에서 사용하는 글쓰기 프롬프트를 기반으로 하여 글쓰기를 하였다. 4종류의 다른 프롬프트가 사용이되어서 편차가 발생할 수 있다.
30분이내에 3문단에 200단어이상으로 구성한 에세이를 작성해야한다. 또한 작성자가 2분이상 활동이 없거나 프롬프트 이외의 작업을 하려고 하면 경고창이 뜨게했습니다.
Keystroke Logging Program
키스트로크 정보를 수집하기위해 JS를 이용하여 만든 프로그램을 통하여 수집을 했다. JS에서 지원하는 addEventListener를 이용하여 수집을 하여 키 입력, 마우스 조작에 대한 값을 수집하게 된다. 이벤트는 순서대로 이벤트 ID에 라벨링을 하며 값을 가지게된다.
Keystroke Measures
Production Rate
글쓰기 생산 비율은 글쓰기 과정에서 시간을 기준으로한 특징(문자, 단어, 문장, …)을 나타내는 비율을 나타낸다.
글쓰기 과정에서 분당 특징(문자, 단어, …)의 갯수를 나타내는 비율
글쓰기가 완성된 상태에서 분당 특징(문자, 단어, …)의 갯수를 나타내는 비율
Pause
일시 중지행동은 2초의 임계값을 가지는 IKI(key down 입력 간의 간격)가 있는것을 의미한다. 이러한 일시중지 활동을 활용하여 아래와 같은 해석이 가능하다.
일시중시 횟수(전체, 분당)
일시중지 시간 비율(전체 시간 대비)
일시중지 길이(전체 일시중지 시간의 평균)
특징(단어, 문장, …)들 사이에서 일시중지 길이 또는 빈도수
Revision
글쓰기에서 수정과 관련된 항목이다. 삭제는 어디서든 단어를 지우는 행위를 칭하며, 삽입은 글쓰기의 마지막 위치가 아닌 위치에서 진행하는 입력을 칭한다.
삭제 횟수(전체, 분당)
삽입 횟수(전체, 분당)
삭제한 길이(단어수)
삽입한 길이(단어수)
삭제 시간 비율(전체 시간 대비)
삽입 시간 비율(전체 시간 대비)
글쓰기 완료 vs 글쓰기 진행에서 변경된 단어수의 비율 비교
글쓰기 완료 후 수정이 진행된 횟수와 길이
수정이 이루어진 직후 이루어진 수정의 횟수, 길이
현재 지점에서 발생한 수정의 횟수
다른 지점에서 발생한 수정의 횟수
Burst
일시 중지 및 수정없이 지속적으로 글쓰기를 진행하는것을 버스트라고 한다. P-버스트는 일시중지를 기준으로 분리가 되는것, R-버스트는 수정을 기준으로 분리가 되는 글씨기 행위이다.
P-버스트의 숫자(전체, 분당)
R-버스트의 숫자(전체, 분당)
P-버스트의 시간 비율(전체 시간 대비)
R-버스트의 시간 비율(전체 시간 대비)
P-버스트의 길이(단어수)
R-버스트의 길이(단어수)
Process Variance
글쓰기의 분산은 글쓰는 과정에서 작성자가 구간별로 유창하게 작성하는것을 보기 위한 기준이다. 5 또는 10과 같이 특정값을 기준으로 전체를 분할하고 분할된 구역에서 생성된 문자의 수(전체, 분당)를 의미한다.
simple EDA
키스트로크 로그 데이터는 아래와 같이 구성이 된다.
id는 에세이를 구별하는 인자
event_id는 해당하는 에세이에서 발생하는 서순을 확인하기 위한 인자
down_time은 해당 키는 누르는 시점의 시간
up_time은 해당 키를 떼는 시점의 시간
action_time는 up_time - down_time
activity는 취한 액션이 어떠한 활동인지를 구별하는 인자
down_event, up_event는 어떤 키스트로크인지 구별하는 인자(단순한 문자는 q로 마스킹 처리됨)
text_change는 해당 event를 통하여 변경된 logs를 나타내는 인자
cursor_position은 깜빡이는 커서가 현재 어디 있는지 나타내는 인자
word_count는 작성된 단어의 갯수
점수 데이터는 아래와 같이 구성이 된다.
id는 에세이를 구별하는 인자
score는 에세이의 점수
키스트로크 로그의 info는 아래와 같이 결측값은 없다.
점수 데이터의 info는 아래와 같이 결측값은 없다.
키스트로크 로그의 describe는 아래와 같다.
점수 데이터의 describe는 아래와 같다.
keystroke EDA
Production Rate
문자, 단어, 문단등의 상황에서의 갯수와 비율을 살펴본다.
Production Rate(character)
문자의 갯수를 따지는 행위를 보면 각 액션에서 text_change가 어떻게 되었는지를 확인하면 볼 수 있다. text_change를 살펴보면 q로 마스킹 된 값, copy&paste와 같이 여러문자를 다루는 “ => “로 구분하는값, Move행위를 통하여 문자들이 이동한 값, 특이값으로 크게 4종류로 구분을 할 수 있다.
이것이 삭제를 하는 행위인지 작성을 하는 행위인지를 확인하기 위하여 logs에 새로운 칼럼을 만드는데 remove, move, cut과 같은 행위는 삭제를 하는 행위로 취급하여 text_change의 길이를 넣고, 작성을 하는 행위는 input, move, paste와 같은 행위로 취급하여 text_change의 길이를 넣었다. move와 같은 행위는 삭제와 작성을 동시에 행하는 작업이기 때문에 두가지 종류에 같이 포함이 된다.
위의 작업을 진행하여보면 문제가 발생하게 된다. \n과 기타 특수문자가 여러개의 문자로 인식되는것이다. 그래서 전처리고 simple_text_change라는 column을 만들어서 \n과 기타 특수문자를 q로 마스킹하는 작업을 진행하고 분류를 하는 작업을 진행하게 되었다.
위의 문자의 변화를 구분하게 됨으로 인하여 우리는 문자의 갯수와 비율을 살펴 볼 수 있게 되었다. 하지만 아직 확장된 개념의 단어, 문단에 대한 이해는 조금 더 EDA를 진행해야 알 수 있다.
Production Rate(word)
Pause
다른 키스트로크와 다르게 가장 직관적인 탐색이 가능한 기법이다. IKI가 2초 이상인것이 pause이기 때문에 이전의 down_time을 down shift를 행하여 현재의 down_time과 비교를 했을때 2초이상의 차이가 나면 is_pause라는 column을 추가로 생성해 True값을 넣어주면서 시작을 한다.
Pause(count)
생성된 is_pause columns을 sum, 시간에 대하여 섹션을 나눠서 sum을 진행하면된다.
Pause(rate)
event_id의 시작과 끝의 시간 차이를 통하여 실제 작업시간을 확인하고 실제작업시간으로 일시중지한 시간의 합을 나눠서 구한다.
Pause(length)
is_pause에 해당하는 값들의 시간을 수치적으로 분석한다(mean, max, min, …)
Pause(per state)
단어의 경우 전, 후에 q의 입력이 있는지
문장의 경우 전, 후에 space의 입력이 있는지
문단의 경우 전, 후에 \n의 입력이 있는지
위의 케이스별로 빈도수와 길이를 구한다.
Revision
삭제의 경우 판단하기가 쉬우며 Production Rate단에서 생성한 삭제된 단어가 존재할 경우 is_deletion을 True로 이루어진 column을 만들면 된다.
삽입의 경우 판단하기가 쉽지 않다. 첫번째로 알아야하는것이 현재 내 커서의 위치가 마지막이 아닌지를 확인하고 Production Rate단에서 input에 해당하는 부분이 존재하는지 확인을 하면 된다.
하지만 이번 대회에서 데이터를 확인해본 결과 작성도중 맨뒤에 스페이스를 놔두고 그 직전에 입력을 하는 등 입력을 하는 위치가 신뢰성이 조금 떨어지는것이 확인 되어서 2가지 가설을 세워 보았다.
작성자가 실수를 한것이다.
맨뒤의 스페이스는 자동으로 지워지는 프롬프트를 사용한것이다.
이것은 cumsum을 이용한 누적글자수와 현재의 커서 위치를 분석하여 커서가 어디에 위치하는지를 분석해보고 스페이스 이후에 revision이 발생하면 스페이스를 제거하는 로직을 구성해 보았으나 오히려 정상적으로 작동하지 않은것을 확인하게 되었습니다.
이후 데이터를 수동으로 추적해본 결과 스페이스가 지워지지않음을 확인하게 되어서 작성자가 맨뒤에 스페이스가 있는것을 잊고 작업을 했음을 알 수 있었습니다. 그렇기 때문에 작성자가 실수한것을 고려하여 revision을 진행할지 아니면 고려하지 않고 진행할지 방향이 두가지로 나뉘어지게 되었습니다.
Revision(deletion)
is_deletion을 sum으로 전부 합한 값, per minute
Revision(insertion)
is_insertion을 sum으로 전부 합한 값, per minute
Revision(deletion length)
is_deletion일때 문자 삭제 columns의 수의 합
Revision(insertion length)
is_insertion일때 문자 생성 columns의 수의 합
Revision(deletion rate)
is_deletion일때 시간의 합을 전체시간으로 나눈값
Revision(insertion rate)
is_insertion일때 시간의 합을 전체시간으로 나눈값
Revision(revisioned character)
문자 생성 columns의 수의 합과 문자 제거 column의 수의 합을 column으로 생성
Revision(revision after producted)
current_cursor의 위치가 max_character의 위치와 같으면서 최대값인 이후에 진행된 event들에서 발생한 revision들의 횟수와 길이
Revision(revision after revision)
is_deletion or is_insertion의 이후에 연속으로 발생한 revision의 횟수, 길이
Revision(revision in same place)
current_cursor를 down diff로 값을 받아서 값이 변경하지 않았 했으면서 event가 Nonproduction가 아닌 revision이 발생하는 경우의 횟수현재 지점에서 발생한 수정의 횟수.
하지만 직전값을 인식하는 방식의 경우 올바르게 작동하지 않을 수 있기때문에 추가적으로 확인이 필요함
Revision(revision in different place)
current_cursor를 down diff로 값을 받아서 값이 변경을 했으면서 event가 Nonproduction(기타 키입력, 마우스 클릭) revision이 발생하는 경우의 횟수.
하지만 직전값을 인식하는 방식의 경우 올바르게 작동하지 않을 수 있기때문에 추가적으로 확인이 필요함
Burst
Burst(p number)
is_pause간의 거리가 1이상인 burst의 숫자(is_pause.sum + 1), 분당 카테고리화 하여 is_pause.sum + 1
Burst(r number)
is_revision간의 거리가 1이상인 burst의 숫자(is_revision.sum + 1), 분당 카테고리화 하여 is_revision.sum + 1
Burst(p rate)
is_pause의 시간의 합을 전체 시간으로 나눠준 값을 1에서 뺀값
Burst(r rate)
is_revision의 시간의 합을 전체 시간으로 나눠준 값을 1에서 뺀값
Burst(p length)
is_pause간의 상태에서 단어의 변화량의 합
Burst(r length)
is_revision간의 상태에서 단어의 변화량의 합
Process Variance
Process Variance(per state)
글쓰기의 분산은 글쓰는 과정에서 작성자가 구간별로 유창하게 작성하는것을 보기 위한 기준이다. 5 또는 10과 같이 특정값을 기준으로 전체를 분할하고 분할된 구역에서 생성된 문자의 수(전체, 분당)를 의미한다.
Review
-
-
3. 종합탐색(1)
개요
지금까지 파이썬을 이용한 몇몇가지 기능들을 공부했고 내용을 확립하기 위하여 간단한 프로젝트를 진행하기로 했다.
시리와 같은 AI 봇은 아니지만 pandas, tts, stt, 크롤링까지 지금까지 배운것들을 토대로 간단하게 챗봇느낌의 프로그램을 제작해보았다.
내용
자료 파트에서 참고하여 보면 아래와 같이 구성이 되어있다.
proj
|
|— main.py
|— const.py
|— bip.mp3
|— /module
— |— api.py
— |— data_search.py
— |— speech_service.py
기본적인 프로세스는 main.py에 변동성이 없는 데이터는 const.py에 그외 중복적으로 작동되는 함수들은 사용처 별로 정리하여 module폴더에 구성하였다.
main/Timo/waitInput
stt를 활용하기 때문에 입력을 받아서 컨트롤하는 함수가 필요함을 느껴서 만들게 되었으며, bip이라는 인자를 이용해서 stt의 입력을 받을 준비가 되어 있는지 사용자가 알 수 있게 만들었습니다.
main/Timo/sst_classifier
로직을 관장하는 주된 함수로 제작하였으며, 로그인, 로그아웃, 검색, 날씨, 미세먼지, 게임, 수위 조사에 접근하여 핸들링하는 것을 가능하게 한다. 검색, 날씨 미세먼지의 경우 XX 검색 또는 검색XX와 같이 사용자에 따라서 사용법이 다를 수 있기에 두가지 모두 가능하게 했습니다. 검색의 경우는 조금 더 나아가 XXX XX 검색과 같이 여러 키워드 검색을 지원합니다.
main/Timo/__sst_classifier_error
sst_classifier를 통하여 분류가 되지 않을때 접근하게 되며 다시 명령을 기다리는 상태가 된다.
main/Timo/__sst_data_error
api와 crawling과 같은 데이터 검색을 진행할때 서버오류나 기타 오류들이 발생하면 접근하게 되며 맨처음 상태로 돌아가게 됩니다.
main/Timo/login
로그인을 하게 되면 접근하게 되며 추가적인 핸들링이 필요할 경우를 대비하여 함수화 하였습니다.
main/Timo/logout
로그아웃을 하게 되면 접근되며 추가 핸들링이 필요하면 작성하게된다.
main/Timo/naver_search
네이버 크롤링을 사용하여 검색결과를 제공하여 준다.
main/Timo/weather_search
날씨를 검색하여 주는 함수로 현재시간을 기준으로 해당 지역의 기상정보를 제공해준다.
main/Timo/pm_search
미세먼지를 검색하여 주는 함수로 검색 방식에는 여러가지로 나뉘게 된다. 단순히 서울에 대한 검색도 가능하며 서울 강남구와 같은 세부 지역도 가능하다. 로직으로는 지명을 토대로 좌표를 구하여 좌표를 기준으로 해당하는 지역의 지역번호를 받게되며, 해당 지역번호로 미세먼지 정보를 받아서 상세분류 작업을 통하여 사용자에게 제공하게 된다.
main/Timo/follow_up_game
끝말 잇기 게임으로 컴퓨터와 단어를 주고 받게 되는데 단어는 일부 제한량을 두어서 컴퓨터도 패배할 수 있게 제작했다.
main/Timo/dam_check
요즘 기습적인 폭우와 같은 문제로 인하여 침수사고가 많이 발생하고 있어 댐정보를 통하여 비교적 미리 확인이 가능하지 않을까 해서 제작을 하게 되었으며, 댐마다 실시간 저수율과 방류량을 알 수있다.
module/api/Request
html, api 정보를 받아서 json의 형태로 변환시켜준다.
module/api/SgisApi/geoCoding
지역명을 받아서 해당지역을 좌표화해 반환해준다.
module/api/SgisApi/transformation
좌표계가 사용하고자 하는좌표계와 다를경우 좌표계를 변조할 필요가 있는데 해당 변조를 도와준다.
module/api/MetroApi
미세먼지 정보를 받아서 돌려주는 함수이다.
module/api/KorDictApi
사전에 단어가 있는지, 해당하는 글자로 시작하는 단어가 있는지 확인해주는 함수이다.
module/api/DamApi
댐정보를 받아오기 위한 함수이다.
module/data_search/Searching/bs_search
bs를 반복적으로 사용하기위하여 만든 함수이다.
module/data_search/Searching/selenium_search
셀레니움을 사용할 것을 대비하여 만든 함수이다.
module/speech_service/SpeechService/stt
stt를 제공하는 함수이다. bip 인자를 통해 bip음을 출력하기도 한다.
module/speech_service/SpeechService/tts
tts를 제공하는 함수이다.
자료
링크
-
2. 멜론차트
웹크롤링을 연습하기 위해서 멜론 1~50위의 내역을 추출해 csv파일로 저장하여 보았다.
{% highlight python %}
import requests
from bs4 import BeautifulSoup as BS
import pandas as pd
headers = {‘User-Agent’:’Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36’}
url = “https://www.melon.com/chart/index.htm”
data = requests.get(url=url, headers=headers)
soup = BS(data.text,’html.parser’)
datas = soup.select(“.service_list_song .wrap_song_info a”)
rows=[]
columns=[]
for index, i in enumerate(datas):
if index / 4 < 50:
if index % 4 == 0:
print(f”순위: {int(index / 4)+1}”)
print(f”제목: {i.text}”)
columns.append(str(int(index / 4)+1)+”위”)
columns.append(i.text)
elif index % 4 == 1:
print(f”가수: {i.text}\n”)
columns.append(i.text)
elif index % 4 == 2:
rows.append(columns)
columns=[]
data = pd.DataFrame(columns=[“순위”, “제목”, “가수”],data=rows)
data.to_csv(“melon_rank.csv”, encoding=”utf-8-sig”, index=False)
{% endhighlight %}
웹크롤링을 연습하기 위해서 셀레니움을 이용하여 월별로 멜론 1~50위의 내역을 추출해보았다.
{% highlight python %}
from bs4 import BeautifulSoup as BS
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
url = “https://www.melon.com/chart/month/index.htm”
if name == “main”:
driver = webdriver.Chrome()
driver.get(url)
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, ‘#conts > div.calendar_prid > div’)))
a = driver.find_element(By.CSS_SELECTOR, value=’#conts > div.calendar_prid > div’)
a.click()
a = driver.find_elements(By.CSS_SELECTOR, value=’#conts > div.calendar_prid > div > div > dl > dd.month_calendar > ul > li’)
for month_index, month in enumerate(a):
month.click()
time.sleep(2)
data_raw = driver.page_source
soup = BS(data_raw, ‘html.parser’)
title_datas = soup.select(“#lst50 > td:nth-child(6) > div > div > div.ellipsis.rank01 > span > a”)
singer_datas = soup.select(“#lst50 > td:nth-child(6) > div > div > div.ellipsis.rank02 > span”)
real_singer_datas = []
for singer_data in singer_datas:
b=singer_data.select(‘a’)
a=””
try:
a = b.text
except:
for i in b:
a += f”{i.text}, “
real_singer_datas.append(a)
else:
real_singer_datas.append(a)
for index, i in enumerate(title_datas):
print(f”{month_index+1}월 {index+1}위 노래: {i.text} 가수: {real_singer_datas[index]}”)
time.sleep(5)
driver.quit()
{% endhighlight %}
-
Touch background to close